﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using NTTDATA.BEENChina.ResultCertification.Checker.Numeric;
using NTTDATA.BEENChina.ResultCertification.PropertyGridUtil;
using NTTDATA.BEENChina.ResultCertification.PropertyGridUtil.CustomObject;

namespace NTTDATA.BEENChina.ResultCertification.Checker
{
    public class VerfityExpression
    {
        /// <summary>
        /// 字符串项目比较表达式
        /// 表达式里面不能有 +-*/
        /// </summary>
        /// <param name="result"></param>
        /// <param name="conditions"></param>
        /// <returns></returns>
        public bool CheckStringExpression(List<List<string>> result, CustomCollection<ExpressionCondition> conditions)
        {
            if (result.Count.Equals(0))
                return false;

            foreach (ExpressionCondition condition in conditions)
            {
                string exp1Value = ParseString(result, this.InfixToPostfix(condition.Expression1));
                string exp2Value = ParseString(result, this.InfixToPostfix(condition.Expression2));



                bool checkRet;
                switch (condition.Compare)
                {
                    case ComparisonOperator.大于:
                        checkRet = !(System.String.CompareOrdinal(exp1Value, exp2Value) > 0 );
                        break;
                    case ComparisonOperator.大于等于:
                        checkRet = !(System.String.CompareOrdinal(exp1Value, exp2Value) >= 0);
                        break;
                    case ComparisonOperator.等于:
                        checkRet = !(exp1Value.Equals(exp2Value));
                        break;
                    case ComparisonOperator.小于:
                        checkRet = !(System.String.CompareOrdinal(exp1Value, exp2Value) < 0);
                        break;
                    case ComparisonOperator.小于等于:
                        checkRet = !(System.String.CompareOrdinal(exp1Value, exp2Value) <= 0);
                        break;
                    case ComparisonOperator.不等于:
                        checkRet = exp1Value.Equals(exp2Value);
                        break;
                    default:
                        return false;
                }
                if (checkRet == true)
                    return true;
            }
            return false;
        }

        /// <summary>
        /// 数值项目比较表达式
        /// </summary>
        /// <param name="result"></param>
        /// <param name="conditions"></param>
        /// <returns></returns>
        public bool CheckNumberExpression(List<List<string>> result, CustomCollection<ExpressionCondition> conditions)
        {
            foreach (ExpressionCondition condition in conditions)
            {
                double exp1Value = this.Parse(result, this.InfixToPostfix(condition.Expression1));
                double exp2Value = this.Parse(result, this.InfixToPostfix(condition.Expression2));

                bool checkRet;
                switch (condition.Compare)
                {
                    case ComparisonOperator.大于:
                        checkRet = !(exp1Value > exp2Value);
                        break;
                    case ComparisonOperator.大于等于:
                        checkRet = !(exp1Value >= exp2Value);
                        break;
                    case ComparisonOperator.等于:
                        checkRet = !(exp1Value.Equals(exp2Value));
                        break;
                    case ComparisonOperator.小于:
                        checkRet = !(exp1Value < exp2Value);
                        break;
                    case ComparisonOperator.小于等于:
                        checkRet = !(exp1Value <= exp2Value);
                        break;
                    case ComparisonOperator.不等于:
                        checkRet = exp1Value.Equals(exp2Value);
                        break;
                    default:
                        return false;
                }
                if (checkRet == true)
                    return true;
            }
            return false;
        }

        #region 中缀转后缀
        /// <summary>
        /// 中缀表达式转换为后缀表达式
        /// </summary>
        /// <param name="expression"></param>
        /// <returns></returns>
        private string InfixToPostfix(string expression)
        {
            Stack<char> operators = new Stack<char>();
            StringBuilder result = new StringBuilder();
            for (int i = 0; i < expression.Length; i++)
            {
                char ch = expression[i];
                if (char.IsWhiteSpace(ch)) continue;
                switch (ch)
                {
                    case '+':
                    case '-':
                        while (operators.Count > 0)
                        {
                            char c = operators.Pop();   //pop Operator
                            if (c == '(')
                            {
                                operators.Push(c);      //push Operator
                                break;
                            }
                            else
                            {
                                result.Append(c);
                            }
                        }
                        operators.Push(ch);
                        result.Append(" ");
                        break;
                    case '*':
                    case '/':
                        while (operators.Count > 0)
                        {
                            char c = operators.Pop();
                            if (c == '(')
                            {
                                operators.Push(c);
                                break;
                            }
                            else
                            {
                                if (c == '+' || c == '-')
                                {
                                    operators.Push(c);
                                    break;
                                }
                                else
                                {
                                    result.Append(c);
                                }
                            }
                        }
                        operators.Push(ch);
                        result.Append(" ");
                        break;
                    case '(':
                        operators.Push(ch);
                        break;
                    case ')':
                        while (operators.Count > 0)
                        {
                            char c = operators.Pop();
                            if (c == '(')
                            {
                                break;
                            }
                            else
                            {
                                result.Append(c);
                            }
                        }
                        break;
                    default:
                        result.Append(ch);
                        break;
                }
            }
            while (operators.Count > 0)
            {
                result.Append(operators.Pop()); //pop All Operator
            }
            return result.ToString();
        }
        #endregion

        #region 求值经典算法

        /// <summary>
        /// 求值的经典算法
        /// </summary>
        /// <param name="result"> </param>
        /// <param name="expression">字符串表达式</param>
        /// <returns></returns>
        private double Parse(List<List<string>> result, string expression)
        {
            //string postfixExpression = InfixToPostfix(expression);
            string postfixExpression = expression;
            Stack<double> results = new Stack<double>();
            double x, y;
            for (int i = 0; i < postfixExpression.Length; i++)
            {
                char ch = postfixExpression[i];
                if (char.IsWhiteSpace(ch)) continue;
                switch (ch)
                {
                    case '+':
                        y = results.Pop();
                        x = results.Pop();
                        results.Push(x + y);
                        break;
                    case '-':
                        y = results.Pop();
                        x = results.Pop();
                        results.Push(x - y);
                        break;
                    case '*':
                        y = results.Pop();
                        x = results.Pop();
                        results.Push(x * y);
                        break;
                    case '/':
                        y = results.Pop();
                        x = results.Pop();
                        results.Push(x / y);
                        break;
                    default:
                        int pos = i;
                        StringBuilder operand = new StringBuilder();
                        do
                        {
                            operand.Append(postfixExpression[pos]);
                            pos++;

                        } while (postfixExpression.Length > pos && !"+-*/(). ".Contains(postfixExpression[pos]));
                        //} while (postfixExpression.Length > pos && (char.IsDigit() || postfixExpression[pos] == '.'));
                        i = --pos;
                        string value = this.GetValue(result, operand.ToString());

                        results.Push(string.IsNullOrEmpty(value) ? 0.0 : double.Parse(value));
                        break;
                }
            }
            return results.Peek();
        }


        private string ParseString(List<List<string>> result, string expression)
        {
            //string postfixExpression = InfixToPostfix(expression);
            string postfixExpression = expression;
            Stack<string> results = new Stack<string>();
            double x, y;
            for (int i = 0; i < postfixExpression.Length; i++)
            {
                char ch = postfixExpression[i];
                if (char.IsWhiteSpace(ch)) continue;
                switch (ch)
                {
                    case '+': 
                    case '-': 
                    case '*': 
                    case '/': 
                        break;
                    default:
                        int pos = i;
                        StringBuilder operand = new StringBuilder();
                        do
                        {
                            operand.Append(postfixExpression[pos]);
                            pos++;

                        } while (postfixExpression.Length > pos && !"+-*/(). ".Contains(postfixExpression[pos]));
                        //} while (postfixExpression.Length > pos && (char.IsDigit() || postfixExpression[pos] == '.'));
                        i = --pos;
                        string value = this.GetValue(result, operand.ToString());

                        results.Push( value );
                        break;
                }
            }
            return results.Peek();
        }
        #endregion


        private string GetValue(List<List<string>> result, string operand)
        {
            if (string.IsNullOrEmpty(operand))
            {
                throw new ExpressionErrorException("行列表示式为空");
            }

            if (isNumberic(operand))
            {
                return operand;
            }

            string[] operands = operand.Trim().Split(new char[] { ',' });
            if (operands.Length != 2)
            {
                throw new ExpressionErrorException("行列表达式错误，" + operand + "不是一个有效的行列表达式");
            }
            int rowIdx = int.Parse(operands[0].Trim());
            int colIdx = int.Parse(operands[1].Trim());

            if (result.Count > rowIdx && result[rowIdx].Count > colIdx)
            { 

                string cellValue = result[rowIdx][colIdx];
                if (string.IsNullOrEmpty(cellValue.Replace("/",string.Empty)) )
                {
                    throw new CellEmptyException();
                }

                return cellValue;

            }
            else
            {
                throw new ExpressionErrorException("行列表达式结果超出录入结果范围： " + operand);
            }
        }

        private bool isNumberic(string s)
        {
            Regex r = new Regex(@"^\d+(\.)?\d*$");
            return (r.IsMatch(s));
        }
    }

    public class ExpressionErrorException : Exception
    {
        public ExpressionErrorException(string message)
            : base(message)
        { }
    }

    public class CellEmptyException : Exception{}
    public class RowEmptyException : Exception { }
}
